/* Sentinel - IRC Statistical and Operator Services
** util.c - Misc. utility functions
**
** Copyright W. Campbell and others.  See README for more details
** Some code Copyright: Jonathan George, Kai Seidler, ircd-hybrid Team,
**                      IRCnet IRCD developers.
**
** $Id: util.c 10 2010-02-02 01:12:49Z dubkat $
*/

#include "stats.h"
#include "struct.h"
#include "proto.h"
#include "match.h"
#include "lang.h"
#include "slog.h"
#include <signal.h>

/* print a period of seconds into human readable format */
char *timediff(time_t u)
{
  static char buf[TIMEBUF];

  if (u > 86400)
  {
    time_t days = u / 86400;

    snprintf(buf, TIMEBUF, "%ld %s, %02ld:%02ld",
             (long)days, (days == 1) ? T_DAY : T_DAYS,
             (long)((u / 3600) % 24), (long)((u / 60) % 60));
  }
  else if (u > 3600)
  {
    time_t hours = u / 3600;
    time_t minutes = (u / 60) % 60;

    snprintf(buf, TIMEBUF, "%ld %s, %ld %s",
             (long)hours, (hours == 1) ? T_HOUR : T_HOURS, (long)minutes,
             (minutes == 1) ? T_MINUTE : T_MINUTES);
  }
  else
  {
    time_t minutes = u / 60;
    time_t seconds = u % 60;

    snprintf(buf, TIMEBUF,
             "%ld %s, %ld %s",
             (long)minutes, (minutes == 1) ? T_MINUTE : T_MINUTES,
             (long)seconds, (seconds == 1) ? T_SECOND : T_SECONDS);
  }

  return buf;
}

/* custom ftime format */
char *sftime(time_t stuff)
{
  static char fmtime[LINE];
  struct tm *ltm = localtime(&stuff);

  if (stuff != 0)
  {
    strftime(fmtime, LINE, FTMFMT, ltm);
  }
  else
  {
    snprintf(fmtime, LINE, "No Time/Record");
  }

  return fmtime;
}

char *irc_time(time_t ts)
{
  static char fmtime[LINE];

  struct tm *ltm = localtime(&ts);

  /* Since we already depend on POSIX locale support, %z in the time
  ** format string is acceptable...GMToffset was a nice function
  ** though :)
  */
  strftime(fmtime, LINE, IRCTFMT, ltm);

  return fmtime;
}

#if defined(IRCD_HYBRID) || defined(IRCD_HYB6) || defined(IRCD_HYB7) || \
    defined(IRCD_HYBSVS) || defined(IRCD_BAHAMUT) || defined(IRCD_RATBOX)
/* A specific tokenizer for SJOIN, since Hybrid 7 likes adding extra spaces */
int sjtoken(char *message, char **parv)
{
  char *next;
  int count;

  if (!message)
  {
    /* Something is seriously wrong...bail */
    parv[0] = NULL;
    return 0;
  }

  /* Now we take the beginning of message and find all the spaces...
  ** set them to \0 and use 'next' to go through the string
  */
  next = message;

  /* Eat any initial delimiters */
  while (*next == ' ')
    next++;

  parv[0] = next;
  count = 1;

  while (*next)
  {
    /* This is fine here, since we don't have a :delimited
    ** parameter like tokenize
    */
    if (count == SJPARA)
    {
      /* We've reached our limit */
      debug("Reached the SJPARA limit!");
      return count;
    }
    if (*next == ' ')
    {
      *next = '\0';
      next++;
      /* Eat any additional delimiters */
      while (*next == ' ')
        next++;
      /* If it's the end of the string, it's simply
      ** an extra space at the end.  Here we break.
      */
      if (*next == '\0')
        break;
      /* If it happens to be a stray \r, break too */
      if (*next == '\r')
        break;
      parv[count] = next;
      count++;
    }
    else
    {
      next++;
    }
  }

  return count;
}
#endif

/* Generate a hash value into the tables */
unsigned long HASH(const unsigned char *name, int size_of_table)
{
  unsigned long h = 0, g;

  while (*name)
  {
    h = (h << 4) + ToLower(*name++);
    if ((g = (h & 0xF0000000)))
      h ^= g >> 24;
    h &= ~g;
  }
  return h % size_of_table;
}

/* Get the version flags string */
char *VersionFlags()
{
  static char ver[LINE];
  char *verptr = ver;

#ifdef TRACK_ADMIN

  *verptr++ = 'A';
#endif
#ifdef SUPPORT_MSG_UH

  *verptr++ = 'U';
#endif
#ifdef SUPPORT_MSG_OPERS

  *verptr++ = 'O';
#endif
#ifdef TRACK_VERSION

  *verptr++ = 'V';
#endif
#ifdef JUPESERV

  *verptr++ = 'J';
#endif
#ifdef ENABLE_QUOTE

  *verptr++ = 'Q';
#endif
#ifdef ENABLE_OP

  *verptr++ = 'o';
#endif
#ifdef ENABLE_TAKEOVER

  *verptr++ = 'K';
#endif
#ifdef TRACK_GECOS

  *verptr++ = 'G';
#endif
#ifdef TRACK_CTCPVERSION

  *verptr++ = 'v';
#endif
#ifdef HIDE_IPS

  *verptr++ = 'H';
#endif
#ifdef SUPPORT_HOSTSERV

  *verptr++ = 'h';
#endif
#ifdef OPER_MODES

  *verptr++ = 'M';
#endif
#ifdef DIE_ON_MASS_COLLIDE

  *verptr++ = 'C';
#endif
#ifdef USE_CURRENT_TS

  *verptr++ = 'e';
#endif
#ifdef DISALLOW_NICK_PRIVMSG

  *verptr++ = 'u';
#endif
#ifdef LISTSERV

  *verptr++ = 'L';
#endif
#ifdef SPLITSERV

  *verptr++ = 'P';
#endif
#ifdef PUBSTATS

  *verptr++ = 'p';
#endif
#ifdef STATSERV

  *verptr++ = 'T';
#endif
#ifdef MONSERV

  *verptr++ = 'm';
#endif
#if defined(MONSERV) && defined(MON_IDENT_CLONES)

  *verptr++ = 'c';
#endif
#if defined(MONSERV) && defined(MON_DRONES)

  *verptr++ = 't';
#endif
#ifdef ALERTMODE

  *verptr++ = 'a';
#endif
#ifdef SPYMODE

  *verptr++ = 's';
#endif
#ifdef USE_OPERSPY

  *verptr++ = 'S';
#endif

  if (IsDebug())
    *verptr++ = 'd';
  if (IsNoFork())
    *verptr++ = 'n';

  *verptr = '\0';

#ifdef USE_ASCII_CASEMAP

  strlcat(ver, "(ASCII) ", LINE);
#else

  strlcat(ver, "(RFC1459) ", LINE);
#endif

#ifdef IRCD_HYBRID

  strlcat(ver, "HYBRID", LINE);
#endif
#ifdef IRCD_HYB6

  strlcat(ver, "HYB6", LINE);
#endif
#ifdef IRCD_HYB7

  strlcat(ver, "HYB7", LINE);
#endif
#ifdef IRCD_RATBOX

  strlcat(ver, "RATBOX", LINE);
#endif
#ifdef IRCD_BAHAMUT

  strlcat(ver, "BAHAMUT", LINE);
#endif
#ifdef IRCD_HYBSVS
#ifdef IRCD_HYBBG

  strlcat(ver, "HYBBG", LINE);
#else

  strlcat(ver, "HYBSVS", LINE);
#endif
#endif

  return ver;
}

#ifdef OPER_MODES
char opermode(struct User *u)
{
  if (u == NULL)
    return '-';
  if (u->operflags & OPER_MYOPER)
    return '*';
  if (u->operflags & OPER_SVSADMIN)
    return 'a';
  if (u->operflags & OPER_ADMIN)
    return 'A';
  if (u->operflags & OPER_GLOBAL)
    return 'O';
  /* Shouldn't get here */
  return 'x';
}
#endif /* OPER_MODES */

void SigHup(int sig)
{
  need_rehash = 1;
  signal(SIGHUP, SigHup);
}

void SigUsr1(int sig)
{
  /* This should be sufficient to tell it to reconnect */
  close(servsock);
  delete_connection(servsock);
  servsock = -1;
  ServTermination();
  signal(SIGUSR1, SigUsr1);
}

void SigTerm(int sig)
{
  wallops(settings.name, "Received SIGTERM: Exiting");
  alert("Received SIGTERM: Exiting");
  sendto_dcc_flag(CF_NOTICES, "Received SIGTERM: Exiting");
#ifdef STATSERV

  sts(":%s QUIT :Received SIGTERM: Exiting", StatServ.nick);
#endif

  sts(":%s QUIT :Received SIGTERM: Exiting", AdminServ.nick);
#ifdef SPLITSERV

  sts(":%s QUIT :Received SIGTERM: Exiting", SplitServ.nick);
#endif
#ifdef JUPESERV

  sts(":%s QUIT :Received SIGTERM: Exiting", JupeServ.nick);
#endif
#ifdef LISTSERV

  sts(":%s QUIT :Received SIGTERM: Exiting", ListServ.nick);
#endif
#ifdef MONSERV

  sts(":%s QUIT :Received SIGTERM: Exiting", MonServ.nick);
#endif

  sleep(1);
  close(servsock);
  close_dccs();
  SaveStats();
  slog(DEFAULT_LOG, L_ALERT, "Received SIGTERM: Exiting");
  close_logfiles();
  /* No need to reinstall the signal handler */
  exit(0);
}

void SigInt(int sig)
{
  wallops(settings.name, "Received SIGINT: Exiting");
  alert("Received SIGINT: Exiting");
  sendto_dcc_flag(CF_NOTICES, "Received SIGINT: Exiting");
#ifdef STATSERV

  sts(":%s QUIT :Received SIGINT: Exiting", StatServ.nick);
#endif

  sts(":%s QUIT :Received SIGINT: Exiting", AdminServ.nick);
#ifdef SPLITSERV

  sts(":%s QUIT :Received SIGINT: Exiting", SplitServ.nick);
#endif
#ifdef JUPESERV

  sts(":%s QUIT :Received SIGINT: Exiting", JupeServ.nick);
#endif
#ifdef LISTSERV

  sts(":%s QUIT :Received SIGINT: Exiting", ListServ.nick);
#endif
#ifdef MONSERV

  sts(":%s QUIT :Received SIGINT: Exiting", MonServ.nick);
#endif

  sleep(1);
  close(servsock);
  close_dccs();
  SaveStats();
  slog(DEFAULT_LOG, L_ALERT, "Received SIGINT: Exiting");
  close_logfiles();
  /* No need to reinstall the signal handler */
  exit(0);
}

int is_channel(char *chan)
{
  if (chan != NULL)
  {
    /* Sadly, a few servers propogate &channels... CSr comes to
    ** mind here.
    */
    if ( (chan[0] == '#') || (chan[0] == '+') || (chan[0] == '&') )
      return TRUE;
    return FALSE;
  }

  /* Shouldn't get here */
  return FALSE;
}

void alert(char *fmt, ...)
{
#ifdef ALERTMODE
  va_list ap;
  char buf[BUFSIZE];

  /* This is simply a crutch... */
  if (me.uplink == NULL)
    return ;
  if (ad_delayed)
    return ;
  va_start(ap, fmt);
  vsnprintf(buf, sizeof(buf), fmt, ap);
  /* snprintf will always NULL terminate */
  notice(ALERTCHAN, AdminServ.nick, "%s", buf);
  va_end(ap);
#endif
}

#ifdef DRONES_CHANNEL
void dronealert(char *fmt, ...)
{
  va_list ap;
  char buf[BUFSIZE];

  if (me.uplink == NULL)
    return ;
  if (ad_delayed)
    return ;
  va_start(ap, fmt);
  vsnprintf(buf, sizeof(buf), fmt, ap);
  /* snprintf will always NULL terminate */
  notice(DRONES_CHANNEL, AdminServ.nick, "%s", buf);
  va_end(ap);
}
#endif

void spy(char *fmt, ...)
{
#ifdef SPYMODE
  va_list ap;
  char buf[BUFSIZE];

  if (me.uplink == NULL)
    return ;
  if (ad_delayed)
    return ;
  va_start(ap, fmt);
  vsnprintf(buf, sizeof(buf), fmt, ap);
  notice(SPYCHAN, AdminServ.nick, "%s", buf);
  va_end(ap);
#endif
}

void debug(char *fmt, ...)
{
  va_list ap;
  char buf[BUFSIZE];

  if (IsDebug())
  {
    va_start(ap, fmt);
    vsnprintf(buf, sizeof(buf), fmt, ap);
    slog(DEFAULT_LOG, L_DEBUG, "DEBUG: %s", buf);
    va_end(ap);
  }
}

void InitLists(void)
{
  hiddenhead.head = NULL;
  hiddenhead.tail = NULL;
  global_oper_list.head = NULL;
  global_oper_list.tail = NULL;
#if defined(MONSERV) && defined(MON_DRONES)

  MonServ.ignore_server_list.head = NULL;
  MonServ.ignore_server_list.tail = NULL;
  MonServ.gecos_drone_list.head = NULL;
  MonServ.gecos_drone_list.tail = NULL;
  MonServ.version_drone_list.head = NULL;
  MonServ.version_drone_list.tail = NULL;
  MonServ.nick_drone_list.head = NULL;
  MonServ.nick_drone_list.tail = NULL;
  MonServ.channel_drone_list.head = NULL;
  MonServ.channel_drone_list.tail = NULL;
  MonServ.username_drone_list.head = NULL;
  MonServ.username_drone_list.tail = NULL;
  MonServ.drone_users.head = NULL;
  MonServ.drone_users.tail = NULL;
#endif
#ifdef ENABLE_TAKEOVER

  takeover_list.head = NULL;
  takeover_list.tail = NULL;
#endif
#ifdef JUPESERV

  server_jupes.head = NULL;
  server_jupes.tail = NULL;
  nick_jupes.head = NULL;
  nick_jupes.tail = NULL;
#endif

  connection_list.head = NULL;
  connection_list.tail = NULL;
  dying_list.head = NULL;
  dying_list.tail = NULL;

#ifdef TRACK_CTCPVERSION

  delayed_users.head = NULL;
  delayed_users.tail = NULL;
#endif
}

/* Is it the midnight hour */
int is_midnight()
{
  struct tm *ltm = localtime(&CurrentTime);

  if (ltm->tm_hour == 0)
    return 1;

  return 0;
}

/* Perform a 3/4 of a second delay using select() */
void select_delay(void)
{
  struct timeval tv;

  tv.tv_usec = 750000; /* 3/4 a second delay */
  tv.tv_sec = 0;       /* ignore this */
  select(1, NULL, NULL, NULL, &tv);
}

